package org.farng.mp3.id3;

import org.farng.mp3.AbstractMP3Tag;
import org.farng.mp3.TagNotFoundException;
import org.farng.mp3.TagOptionSingleton;
import org.farng.mp3.TagUtility;


import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;

import java.util.Iterator;


/**
 * Title:       ID3v1 Description: This class is for an ID3v1 Tag Copyright:
 * Copyright (c) 2002 Company:
 *
 * @author Eric Farng
 * @version $Revision: 1.6 $
 */
public class ID3v1 extends AbstractID3v1 {
    /** DOCUMENT ME! */
    protected String album = "";

    /** DOCUMENT ME! */
    protected String artist = "";

    /** DOCUMENT ME! */
    protected String comment = "";

    /** DOCUMENT ME! */
    protected String title = "";

    /** DOCUMENT ME! */
    protected String year = "";

    /** DOCUMENT ME! */
    protected byte genre = -1;

    /**
     * Creates a new ID3v1 object.
     */
    public ID3v1() {}

    public ID3v1(ID3v1 copyObject) {
        super(copyObject);
        this.album   = new String(copyObject.album);
        this.artist  = new String(copyObject.artist);
        this.comment = new String(copyObject.comment);
        this.title   = new String(copyObject.title);
        this.year    = new String(copyObject.year);
        this.genre   = copyObject.genre;
    }

    public ID3v1(AbstractMP3Tag mp3tag) {
        if (mp3tag != null) {
            ID3v1_1 convertedTag;

            if (mp3tag instanceof ID3v1) {
                throw new UnsupportedOperationException("Copy Constructor not called. Please type cast the argument");
            }

            if (mp3tag instanceof ID3v1_1) {
                convertedTag = (ID3v1_1) mp3tag;
            } else {
                convertedTag = new ID3v1_1(mp3tag);
            }

            this.album   = new String(convertedTag.album);
            this.artist  = new String(convertedTag.artist);
            this.comment = new String(convertedTag.comment);
            this.title   = new String(convertedTag.title);
            this.year    = new String(convertedTag.year);
            this.genre   = convertedTag.genre;
        }
    }

    /**
     * Creates a new ID3v1 object.
     *
     * @param file DOCUMENT ME!
     *
     * @throws TagNotFoundException DOCUMENT ME!
     * @throws IOException DOCUMENT ME!
     */
    public ID3v1(RandomAccessFile file)
          throws TagNotFoundException, IOException {
        this.read(file);
    }

    /**
     * DOCUMENT ME!
     *
     * @param album DOCUMENT ME!
     */
    public void setAlbum(String album) {
        this.album = TagUtility.truncate(album, 30);
    }

    /**
     * DOCUMENT ME!
     *
     * @return DOCUMENT ME!
     */
    public String getAlbum() {
        return album;
    }

    /**
     * DOCUMENT ME!
     *
     * @param artist DOCUMENT ME!
     */
    public void setArtist(String artist) {
        this.artist = TagUtility.truncate(artist, 30);
    }

    /**
     * DOCUMENT ME!
     *
     * @return DOCUMENT ME!
     */
    public String getArtist() {
        return artist;
    }

    /**
     * DOCUMENT ME!
     *
     * @param comment DOCUMENT ME!
     */
    public void setComment(String comment) {
        this.comment = TagUtility.truncate(comment, 30);
    }

    /**
     * DOCUMENT ME!
     *
     * @return DOCUMENT ME!
     */
    public String getComment() {
        return comment;
    }

    /**
     * DOCUMENT ME!
     *
     * @param genre DOCUMENT ME!
     */
    public void setGenre(byte genre) {
        this.genre = genre;
    }

    /**
     * DOCUMENT ME!
     *
     * @return DOCUMENT ME!
     */
    public byte getGenre() {
        return genre;
    }

    /**
     * DOCUMENT ME!
     *
     * @param file DOCUMENT ME!
     *
     * @return DOCUMENT ME!
     *
     * @throws IOException DOCUMENT ME!
     */
    public ID3v1 getID3tag(RandomAccessFile file)
                    throws IOException {
        ID3v1 id3v1tag = new ID3v1_1();

        // look for id3v1_1 tag
        if (id3v1tag.seek(file) == true) {
            try {
                id3v1tag.read(file);
                id3v1tag.delete(file);
            } catch (TagNotFoundException ex) {
                id3v1tag = null;
            }
        } else {
            id3v1tag = null;
        }

        if (id3v1tag == null) {
            // look for id3v1 tag
            id3v1tag = new ID3v1();

            if (id3v1tag.seek(file) == true) {
                try {
                    id3v1tag.read(file);
                    id3v1tag.delete(file);
                } catch (TagNotFoundException ex) {
                    id3v1tag = null;
                }
            } else {
                id3v1tag = null;
            }
        }

        return id3v1tag;
    }

    /**
     * DOCUMENT ME!
     *
     * @return DOCUMENT ME!
     */
    public String getIdentifier() {
        return "ID3v1.00";
    }

    /**
     * DOCUMENT ME!
     *
     * @return DOCUMENT ME!
     */
    public int getSize() {
        return 128;
    }

    /**
     * DOCUMENT ME!
     *
     * @param title DOCUMENT ME!
     */
    public void setTitle(String title) {
        this.title = TagUtility.truncate(title, 30);
    }

    /**
     * DOCUMENT ME!
     *
     * @return DOCUMENT ME!
     */
    public String getTitle() {
        return title;
    }

    /**
     * DOCUMENT ME!
     *
     * @param year DOCUMENT ME!
     */
    public void setYear(String year) {
        this.year = TagUtility.truncate(year, 4);
    }

    /**
     * DOCUMENT ME!
     *
     * @return DOCUMENT ME!
     */
    public String getYear() {
        return year;
    }

    /**
     * DOCUMENT ME!
     *
     * @param tag DOCUMENT ME!
     */
    public void append(AbstractMP3Tag tag) {
        ID3v1 oldTag = this;
        ID3v1 newTag = null;

        if (tag != null) {
            if (tag instanceof ID3v1) {
                newTag = (ID3v1) tag;
            } else {
                newTag = new ID3v1();
            }

            if (tag instanceof org.farng.mp3.lyrics3.AbstractLyrics3) {
                TagOptionSingleton.getInstance().setId3v1SaveYear(false);
                TagOptionSingleton.getInstance().setId3v1SaveComment(false);
            }

            oldTag.title   = (TagOptionSingleton.getInstance().isId3v1SaveTitle() && (oldTag.title.length() == 0)) ? newTag.title : oldTag.title;
            oldTag.artist  = (TagOptionSingleton.getInstance().isId3v1SaveArtist() && (oldTag.artist.length() == 0)) ? newTag.artist : oldTag.artist;
            oldTag.album   = (TagOptionSingleton.getInstance().isId3v1SaveAlbum() && (oldTag.album.length() == 0)) ? newTag.album : oldTag.album;
            oldTag.year    = (TagOptionSingleton.getInstance().isId3v1SaveYear() && (oldTag.year.length() == 0)) ? newTag.year : oldTag.year;
            oldTag.comment = (TagOptionSingleton.getInstance().isId3v1SaveComment() && (oldTag.comment.length() == 0)) ? newTag.comment : oldTag.comment;
            oldTag.genre   = (TagOptionSingleton.getInstance().isId3v1SaveGenre() && (oldTag.genre < 0)) ? newTag.genre : oldTag.genre;

            // we don't need to reset the tag options because
            // we want to save all fields (default)
        }

        //super.append(newTag);
    }

    /**
     * DOCUMENT ME!
     *
     * @param file DOCUMENT ME!
     *
     * @throws IOException DOCUMENT ME!
     */

//    public void append(RandomAccessFile file)
//                throws IOException, TagException {
//        ID3v1 oldTag;
//
//        try {
//            oldTag = new ID3v1(file);
//            oldTag.append(this);
//            oldTag.write(file);
//        } catch (TagNotFoundException ex) {
//            oldTag = null;
//        }
//    }

    /**
     * DOCUMENT ME!
     *
     * @param file DOCUMENT ME!
     *
     * @throws IOException DOCUMENT ME!
     */
    public void delete(RandomAccessFile file)
                throws IOException {
        if (seek(file)) {
            file.setLength(file.length() - 128);
        }
    }

    /**
     * DOCUMENT ME!
     *
     * @param obj DOCUMENT ME!
     *
     * @return DOCUMENT ME!
     */
    public boolean equals(Object obj) {
        if ((obj instanceof ID3v1) == false) {
            return false;
        }

        ID3v1 object = (ID3v1) obj;

        if (this.album.equals(object.album) == false) {
            return false;
        }

        if (this.artist.equals(object.artist) == false) {
            return false;
        }

        if (this.comment.equals(object.comment) == false) {
            return false;
        }

        if (this.genre != object.genre) {
            return false;
        }

        if (this.title.equals(object.title) == false) {
            return false;
        }

        if (this.year.equals(object.year) == false) {
            return false;
        }

        return super.equals(obj);
    }

    /**
     * DOCUMENT ME!
     *
     * @return DOCUMENT ME!
     */
    public Iterator iterator() {
        return new ID3v1Iterator(this);
    }

    /**
     * DOCUMENT ME!
     *
     * @param tag DOCUMENT ME!
     */
    public void overwrite(AbstractMP3Tag tag) {
        ID3v1 oldTag = this;
        ID3v1 newTag = null;

        if (tag != null) {
            if (tag instanceof ID3v1) {
                newTag = (ID3v1) tag;
            } else {
                newTag = new ID3v1();
            }

            if (tag instanceof org.farng.mp3.lyrics3.AbstractLyrics3) {
                TagOptionSingleton.getInstance().setId3v1SaveYear(false);
                TagOptionSingleton.getInstance().setId3v1SaveComment(false);
            }

            oldTag.title   = TagOptionSingleton.getInstance().isId3v1SaveTitle() ? newTag.title : oldTag.artist;
            oldTag.artist  = TagOptionSingleton.getInstance().isId3v1SaveArtist() ? newTag.artist : oldTag.artist;
            oldTag.album   = TagOptionSingleton.getInstance().isId3v1SaveAlbum() ? newTag.album : oldTag.album;
            oldTag.year    = TagOptionSingleton.getInstance().isId3v1SaveYear() ? newTag.year : oldTag.year;
            oldTag.comment = TagOptionSingleton.getInstance().isId3v1SaveComment() ? newTag.comment : oldTag.comment;
            oldTag.genre   = TagOptionSingleton.getInstance().isId3v1SaveGenre() ? newTag.genre : oldTag.genre;

            // we don't need to reset the tag options because
            // we want to save all fields (default)
        }

        //super.overwrite(newTag);
    }

    /**
     * DOCUMENT ME!
     *
     * @param file DOCUMENT ME!
     *
     * @throws IOException DOCUMENT ME!
     * @throws IOException DOCUMENT ME!
     */

//    public void overwrite(RandomAccessFile file)
//                   throws IOException, TagException {
//        ID3v1 oldTag;
//
//        try {
//            oldTag = new ID3v1(file);
//            oldTag.overwrite(this);
//            oldTag.write(file);
//        } catch (TagNotFoundException ex) {
//	    // no more super classes
//	    //super.overwrite(file);
//        }
//    }

    /**
     * DOCUMENT ME!
     *
     * @param file DOCUMENT ME!
     *
     * @throws TagNotFoundException DOCUMENT ME!
     * @throws IOException DOCUMENT ME!
     */
    public void read(RandomAccessFile file)
              throws TagNotFoundException, IOException {
        byte[] buffer = new byte[30];

        if (seek(file) == false) {
            throw new TagNotFoundException("ID3v1 tag not found");
        }

        file.read(buffer, 0, 30);
        title = new String(buffer, 0, 30).trim();

        file.read(buffer, 0, 30);
        artist = new String(buffer, 0, 30).trim();

        file.read(buffer, 0, 30);
        album = new String(buffer, 0, 30).trim();

        file.read(buffer, 0, 4);
        year = new String(buffer, 0, 4).trim();

        file.read(buffer, 0, 30);
        comment = new String(buffer, 0, 30).trim();

        file.read(buffer, 0, 1);
        genre = buffer[0];
    }

    /**
     * DOCUMENT ME!
     *
     * @param file DOCUMENT ME!
     *
     * @return DOCUMENT ME!
     *
     * @throws IOException DOCUMENT ME!
     */
    public boolean seek(RandomAccessFile file)
                 throws IOException {
        byte[] buffer = new byte[3];

        // If there's a tag, it's 127 bytes long and we'll find the tag
        file.seek(file.length() - 128);

        // read the TAG value
        file.read(buffer, 0, 3);

        String tag = new String(buffer, 0, 3);

        return tag.equals("TAG");
    }

    /**
     * DOCUMENT ME!
     *
     * @return DOCUMENT ME!
     */
    public String toString() {
        String str = getIdentifier() + " " + this.getSize() + "\n";

        str += ("Title = " + title + "\n");
        str += ("Artist = " + artist + "\n");
        str += ("Album = " + album + "\n");
        str += ("Comment = " + comment + "\n");
        str += ("Year = " + year + "\n");
        str += ("Genre = " + genre + "\n");

        return str;
    }

    /**
     * DOCUMENT ME!
     *
     * @param tag DOCUMENT ME!
     */
    public void write(AbstractMP3Tag tag) {
        ID3v1 oldTag = this;
        ID3v1 newTag = null;

        if (tag != null) {
            if (tag instanceof ID3v1) {
                newTag = (ID3v1) tag;
            } else {
                newTag = new ID3v1_1(tag);
            }

            oldTag.title   = newTag.title;
            oldTag.artist  = newTag.artist;
            oldTag.album   = newTag.album;
            oldTag.year    = newTag.year;
            oldTag.comment = newTag.comment;
            oldTag.genre   = newTag.genre;
        }

        //super.write(newTag);
    }

    /**
     * DOCUMENT ME!
     *
     * @param file DOCUMENT ME!
     *
     * @throws IOException DOCUMENT ME!
     */
    public void write(RandomAccessFile file)
               throws IOException {
        byte[] buffer = new byte[128];
        Charset cset = Charset.forName("GBK");
        ByteBuffer tmpBf;
        int    i;
        int    offset = 3;
        String str;

        delete(file);
        file.seek(file.length());

        buffer[0] = (byte) 'T';
        buffer[1] = (byte) 'A';
        buffer[2] = (byte) 'G';

        if (TagOptionSingleton.getInstance().isId3v1SaveTitle()) {
            str = TagUtility.truncate(title, 30);
            tmpBf = cset.encode(str);
            for (i = 0; i < tmpBf.array().length; i++) {
                buffer[i + offset] = tmpBf.array()[i];
            }
        }

        offset += 30;

        if (TagOptionSingleton.getInstance().isId3v1SaveArtist()) {
            str = TagUtility.truncate(artist, 30);
            tmpBf = cset.encode(str);
            for (i = 0; i < tmpBf.array().length; i++) {
                buffer[i + offset] = tmpBf.array()[i];
            }
        }

        offset += 30;

        if (TagOptionSingleton.getInstance().isId3v1SaveAlbum()) {
            str = TagUtility.truncate(album, 30);
            tmpBf = cset.encode(str);
            for (i = 0; i < tmpBf.array().length; i++) {
                buffer[i + offset] = tmpBf.array()[i];
            }
        }

        offset += 30;

        if (TagOptionSingleton.getInstance().isId3v1SaveYear()) {
            str = TagUtility.truncate(year, 4);
            tmpBf = cset.encode(str);
            for (i = 0; i < tmpBf.array().length; i++) {
                buffer[i + offset] = tmpBf.array()[i];
            }
        }

        offset += 4;

        if (TagOptionSingleton.getInstance().isId3v1SaveComment()) {
            str = TagUtility.truncate(comment, 30);
            tmpBf = cset.encode(str);
            for (i = 0; i < tmpBf.array().length; i++) {
                buffer[i + offset] = tmpBf.array()[i];
            }
        }

        offset += 30;

        if (TagOptionSingleton.getInstance().isId3v1SaveGenre()) {
            buffer[offset] = genre;
        }
        
        file.write(buffer);
    }
}